package cn.edu.swu.mhans.room.bean.factory;

import cn.edu.swu.mhans.room.bean.PropertyValue;
import cn.edu.swu.mhans.room.bean.RoomBeanCollectionReference;
import cn.edu.swu.mhans.room.bean.RoomBeanDefinition;
import cn.edu.swu.mhans.room.bean.RoomBeanReference;
import com.google.common.base.Strings;
import org.apache.commons.beanutils.BeanUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author ZhanJingbo
 * @version 1.0.0
 * Created on 2018/3/28
 */
public class AutowireCapableRoomBeanFactory extends AbstractRoomBeanFactory {

    private static final String NAME_PROPERTY = "name";

    /**
     * 实例化RoomBean，子类实现
     *
     * @param roomBeanDefinition bean的描述
     * @return 实例化的对象
     */
    @Override
    protected Object doCreateRoomBean(RoomBeanDefinition roomBeanDefinition) throws Exception {
        Object roomBean = createRoomBeanInstance(roomBeanDefinition);
        applyPropertyValues(roomBean, roomBeanDefinition);
        return roomBean;
    }

    private Object createRoomBeanInstance(RoomBeanDefinition roomBeanDefinition) throws IllegalAccessException, InstantiationException {
        return roomBeanDefinition.getRoomBeanClass().newInstance();
    }

    /**
     * 属性拼装
     *
     * @param roomBean           待注入的bean
     * @param roomBeanDefinition bean的描述
     * @throws Exception 抛出异常
     */
    private void applyPropertyValues(Object roomBean, RoomBeanDefinition roomBeanDefinition) throws Exception {
        BeanUtils.setProperty(roomBean, NAME_PROPERTY, roomBeanDefinition.getRoomBeanName());
        if (null == roomBeanDefinition.getPropertyValues()) {
            return;
        }
        List<PropertyValue> propertyValueList = roomBeanDefinition.getPropertyValues().getPropertyValueList();
        for (PropertyValue propertyValue : propertyValueList) {
            String propertyName = propertyValue.getName();
            Object value = propertyValue.getValue();
            if (value instanceof RoomBeanReference) {
                value = handleRoomBeanReference((RoomBeanReference) value);
            }
            if (value instanceof RoomBeanCollectionReference) {
                value = handleRoomBeanCollectionReference((RoomBeanCollectionReference) value);
            }
            BeanUtils.setProperty(roomBean, propertyName, value);
        }
    }


    private Object handleRoomBeanReference(RoomBeanReference roomBeanReference) throws Exception {
        Object valueBean = this.getRoomBeanMap().get(roomBeanReference.getReferenceRoomBeanName());
        if (null == valueBean) {
            valueBean = getRoomBean(roomBeanReference.getReferenceRoomBeanName());
        }
        return valueBean;
    }

    private Object handleRoomBeanCollectionReference(RoomBeanCollectionReference roomBeanCollectionReference) throws Exception {
        Object value = null;
        if (!Strings.isNullOrEmpty(roomBeanCollectionReference.getItemTypeClassName())) {
            return roomBeanCollectionReference.getCollectionObject();
        }
        if (RoomBeanCollectionReference.RoomBeanCollectionReferenceType.LIST.getValue() == roomBeanCollectionReference.getCollectionType()) {
            List<Object> valueList = new ArrayList<>();
            for (Object obj : (List) roomBeanCollectionReference.getCollectionObject()) {
                if (obj instanceof RoomBeanReference) {
                    RoomBeanReference roomBeanReference = (RoomBeanReference) obj;
                    valueList.add(getRoomBean(roomBeanReference.getReferenceRoomBeanName()));
                }
            }
            value = valueList;
        }
        if (RoomBeanCollectionReference.RoomBeanCollectionReferenceType.MAP.getValue() == roomBeanCollectionReference.getCollectionType()) {
            Map<String, Object> valueMap = new HashMap<>();
            Map<String, Object> propertyMap = (Map<String, Object>) roomBeanCollectionReference.getCollectionObject();
            for (Map.Entry<String, Object> entry : propertyMap.entrySet()) {
                String key = entry.getKey();
                if (entry.getValue() instanceof RoomBeanReference) {
                    RoomBeanReference roomBeanReference = (RoomBeanReference) entry.getValue();
                    Object valueObj = getRoomBean(roomBeanReference.getReferenceRoomBeanName());
                    valueMap.put(key, valueObj);
                }
            }
            value = valueMap;
        }
        return value;
    }
}
